using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using Framework;
using UnityEngine.Build.Pipeline;
using Newtonsoft.Json;

namespace FrameworkEditor
{
    public class AssetBundlesEncryptTool
    {
        /// <summary>
        /// 存放ab的临时目录
        /// </summary>
        internal static string AssetBundlesDirName = "AssetBundles";
        [MenuItem("SLTools/AssetBundlesEncryptTool/TestEncryptAb")]
        public static void TestEncryptAndroidAb()
        {
            EncryptAb(FrameworkSettings.AndroidPlayer);
        }

        [MenuItem("SLTools/AssetBundlesEncryptTool/TestEncryptWinAb")]
        public static void TestEncryptWinAb()
        {
            EncryptAb(FrameworkSettings.WindowsPlayer);
        }
        public static void EncryptAb(int platformId)
        {
            string platformName = FrameworkSettings.PlatformIdToName(platformId);
            var abRootDir = FrameworkSettings.OutResLibName + "/" + platformName + "/" + AssetBundlesDirName;
            var abRootPath = Application.dataPath.Replace("Assets", abRootDir);
            var encryptAbRootDir = FrameworkSettings.OutResLibName + "/" + platformName + "/" + FrameworkSettings.AssetBundleInPackage;
            var encryptAbRootPath = Application.dataPath.Replace("Assets", encryptAbRootDir);
            Dictionary<string, string> lookupTab = new Dictionary<string, string>();
            Dictionary<string, string> bundleHashTab = new Dictionary<string, string>();
            HashSet<string> unsupportedSet = BuildLangConfig.GetUnsupportedLangResRealPath();
            foreach (string filePath in Directory.GetFiles(abRootPath, "*" + FrameworkSettings.AssetBundleExt, SearchOption.AllDirectories))
            {
                var assetPath = EditorUtils.GetRelativePathToDir(filePath, abRootPath);
                var assetName = assetPath.Substring(0, assetPath.Length - FrameworkSettings.AssetBundleExt.Length);
                var md5Name = MD5Util.ComputeMd5(assetName);
                string encryptPath = encryptAbRootPath + "/" + md5Name + FrameworkSettings.AssetBundleExt;

                //UnityEngine.Debug.Log("filePath = " + filePath + "\n" + " assetPath = " + assetPath + "\n" + " assetName = " + assetName + "\n" + " md5Name = " + md5Name + "\n");
                if (unsupportedSet.Contains(assetName))
                {
                    FileHelper.DeleteFile(encryptPath);
                    continue;
                }
                byte Key = GetAbEncryptKey(md5Name);
                var data = File.ReadAllBytes(filePath);
                for (int idx = 0; idx < data.Length; idx++)
                {
                    data[idx] ^= Key;
                }

                FileHelper.WriteAllBytesToPath(encryptPath, data);
                lookupTab.Add(assetName, md5Name);

                string bundleMd5 = MD5Util.ComputeMd5Str(data).ToLower();
                bundleHashTab.Add("bundles/" + md5Name + FrameworkSettings.DataExt, bundleMd5);
            }
            string backupDirName = platformName + "_backup";
            string backupDirPath = Application.dataPath.Replace("Assets", FrameworkSettings.OutResLibName + "/" + backupDirName);
            FileHelper.EnsureDir(backupDirPath);
            FileHelper.WriteTextToPath(backupDirPath + "/abLookupTab.json", JsonHelper.Serialize(lookupTab));

            //保存当前构建的所有ab对应的md5到配置目录，方便在设备上校验文件
            string configPath = FrameworkSettings.OutResLibName + "/" + platformName + "/configs/metainfo" + FrameworkSettings.DataExt;
            configPath = Application.dataPath.Replace("Assets", configPath);
            string metainfo = JsonHelper.Serialize(bundleHashTab);
            FileHelper.WriteTextToPath(backupDirPath + "/metainfo.json", metainfo);
            byte[] zipJson = UnityZipUtil.ZipStr(metainfo);
            FileHelper.WriteAllBytesToPath(configPath, zipJson);

            if (FrameworkSettings.UseSBP)
            {
                string logPath = Application.dataPath.Replace("Assets", FrameworkSettings.OutResLibName + "/" + platformName + "_buildlogtep.json");
                if (FileHelper.IsFileExists(logPath))
                {
                    FileHelper.MoveFile(logPath, backupDirPath + "/" + platformName + "_buildlogtep.json");
                }
            }
        }

        private static byte GetAbEncryptKey(string md5Name)
        {
            int sum = 0;
            foreach (char item in md5Name)
            {
                sum += (byte)item;
            }
            sum = (sum % 2 == 0) ? (sum + 2) : (sum + 4);
            return (byte)sum;
        }

        public static void DecryptAb(int platformId)
        {
            string platformName = FrameworkSettings.PlatformIdToName(platformId);
            var abRootDir = FrameworkSettings.OutResLibName + "/" + platformName + "-" + AssetBundlesDirName;
            var encryptAbRootDir = FrameworkSettings.OutResLibName + "/" + platformName + "/" + FrameworkSettings.AssetBundleInPackage;
            string lookupTabPath = Application.dataPath.Replace("Assets", FrameworkSettings.OutResLibName + "/" + platformName + "_lookupTab.json");
            var text = FileHelper.ReadText(lookupTabPath);
            if (FileHelper.IsDirExists(abRootDir))
            {
                Directory.Delete(abRootDir, true);
            }
            Dictionary<string, object> lookupTab = JsonHelper.DerializeAsDic(text);
            foreach (var one in lookupTab)
            {
                string filePath = one.Key;
                string md5Name = (string)one.Value;

                var data = File.ReadAllBytes(encryptAbRootDir + "/" + md5Name + FrameworkSettings.AssetBundleExt);

                byte Key = GetAbEncryptKey(md5Name);
                for (int idx = 0; idx < data.Length; idx++)
                {
                    data[idx] ^= Key;
                }
                FileHelper.EnsureDirForFile(abRootDir + "/" + filePath + FrameworkSettings.AssetBundleExt);
                FileHelper.WriteAllBytesToPath(abRootDir + "/" + filePath + FrameworkSettings.AssetBundleExt, data);
            }
        }
    }
}